package com.sq.util;

import com.sq.util.bo.TimeRangeBO;
import com.sq.util.exception.CommonException;
import com.sq.util.exception.VisualException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

public class DateUtil {

    private static Logger log = LoggerFactory.getLogger(DateUtil.class);

    /**
     * 固定的字符，不能变
     */
    private static final char[] CHAR_ARRAY = "0123456789ABCDEFGHIJKLMNOPQRSTUV".toCharArray();

    /**
     * 按dayRange切分timeRangeBO,精确到秒，start,end 区间双闭
     */
    public static List<TimeRangeBO> splitWithAllClosed(TimeRangeBO timeRangeBO, int dayRange) {
        LocalDateTime start = timeRangeBO.getStart();
        LocalDateTime end = timeRangeBO.getEnd();
        CommonException.isTrue(end.isAfter(start), "end 需要晚于 start");
        CommonException.isTrue(dayRange > 0, "dayRange需要>0");
        LocalDateTime t = start.plusDays(dayRange);
        List<TimeRangeBO> list = new LinkedList<>();
        while (t.isBefore(end)) {
            TimeRangeBO e = new TimeRangeBO();
            e.setStart(start);
            e.setEnd(t.minusSeconds(1));
            list.add(e);
            start = t;
            t = t.plusDays(dayRange);
        }
        TimeRangeBO e = new TimeRangeBO();
        e.setStart(start);
        e.setEnd(end);
        list.add(e);
        return list;
    }

    /**
     * 按dayRange切分timeRangeBO,start 闭,end 开
     */
    public static List<TimeRangeBO> splitWithRightOpen(TimeRangeBO timeRangeBO, int dayRange) {
        LocalDateTime start = timeRangeBO.getStart();
        LocalDateTime end = timeRangeBO.getEnd();
        CommonException.isTrue(end.isAfter(start), "end 需要晚于 start");
        CommonException.isTrue(dayRange > 0, "dayRange需要>0");
        LocalDateTime t = start.plusDays(dayRange);
        List<TimeRangeBO> list = new LinkedList<>();
        while (t.isBefore(end)) {
            TimeRangeBO e = new TimeRangeBO();
            e.setStart(start);
            e.setEnd(t);
            list.add(e);
            start = t;
            t = t.plusDays(dayRange);
        }
        TimeRangeBO e = new TimeRangeBO();
        e.setStart(start);
        e.setEnd(end);
        list.add(e);
        return list;
    }

    public static ZonedDateTime toZonedDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return ZonedDateTime.ofInstant(instant, zoneId);
    }

    public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        return ZonedDateTime.of(localDateTime, zoneId);
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }

    public static LocalDate toLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDate();
    }

    public static long getSecond(LocalDateTime ldt) {
        ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
        return zdt.toEpochSecond();
    }

    public static long getSecond(LocalDate ld) {
        return getSecond(toLocalDateTime(ld));
    }

    public static long getMilli(LocalDateTime ldt) {
        return ldt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    public static long getMilli(LocalDate ld) {
        return getMilli(toLocalDateTime(ld));
    }

    public static boolean isBetween(LocalDate date, LocalDate start, LocalDate end) {
        VisualException.isTrue(end.isAfter(start), "参数错误，end早于start");
        if (date.isBefore(start)) {
            return false;
        }
        if (date.isAfter(end)) {
            return false;
        }
        return true;
    }

    public static boolean isBetween(LocalDateTime date, LocalDateTime start, LocalDateTime end) {
        VisualException.isTrue(!end.isBefore(start), "参数错误，end早于start");
        if (date.isBefore(start)) {
            return false;
        }
        if (date.isAfter(end)) {
            return false;
        }
        return true;
    }

    /**
     * 获得currentTimeMillis的压缩字符串
     */
    public static String getTimeString() {
        return getTimeString(System.currentTimeMillis());
    }

    /**
     * 获得currentTimeMillis的压缩字符串
     */
    public static String getTimeString(long time) {
        StringBuilder str = new StringBuilder();
        int length = CHAR_ARRAY.length;
        while (time != 0) {
            // str.append(RANDOM_ARRAY[(int) (time % length)]);
            // 这个更快
            str.append(CHAR_ARRAY[(int) (time & 31)]);
            time = time / length;
        }
        return str.toString();
    }

    /**
     * 返回压缩字符串对应的时间
     */
    public static long getTime(String compressorStr) {
        long result = 0;
        int length = CHAR_ARRAY.length;
        for (int i = compressorStr.length() - 1; i >= 0; i--) {
            char c = compressorStr.charAt(i);
            int index = getCharIndex(c);
            result = length * result + index;
        }
        return result;
    }

    private final static DateTimeFormatter DATE_FORMATETER = DateTimeFormatter.ofPattern(DateFormats.DATE_FORMAT);

    private final static DateTimeFormatter DATE_TIME_FORMATETER = DateTimeFormatter
            .ofPattern(DateFormats.DATE_TIME_FORMAT);

    private final static DateTimeFormatter ISO_TIME_FORMATETER = DateTimeFormatter.ofPattern(DateFormats.ISO_FORMAT);

    public static LocalDate toLocalDate(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        int dayOfMonth = localDateTime.getDayOfMonth();
        return LocalDate.of(year, month, dayOfMonth);
    }

    public static LocalDate toLocalDate(String dateStr, DateTimeFormatter formatter) {
        return LocalDate.parse(dateStr, formatter);
    }

    public static LocalDate toLocalDate(String dateStr) {
        return LocalDate.parse(dateStr, DATE_FORMATETER);
    }

    public static LocalDateTime toLocalDateTime(LocalDate localDate) {
        int year = localDate.getYear();
        Month month = localDate.getMonth();
        int dayOfMonth = localDate.getDayOfMonth();
        return LocalDateTime.of(year, month, dayOfMonth, 0, 0, 0, 0);
    }

    public static LocalDateTime toLocalDateTime(String dateStr, DateTimeFormatter formatter) {
        return LocalDateTime.parse(dateStr, formatter);
    }

    public static LocalDateTime toLocalDateTime(String dateStr) {
        return LocalDateTime.parse(dateStr, DATE_TIME_FORMATETER);
    }

    /**
     * 返回yyyy-MM-dd
     */
    public static String format(LocalDate date) {
        return date.format(DATE_FORMATETER);
    }

    /**
     * 返回yyyy-MM-dd HH:mm:ss
     */
    public static String format(LocalDateTime date) {
        return date.format(DATE_TIME_FORMATETER);
    }

    public static String isoFormat(ZonedDateTime date) {
        return date.format(ISO_TIME_FORMATETER);
    }

    /**
     * 日期增减
     */
    public static LocalDate plusDays(LocalDate localDate, int dayNum) {
        return localDate.plusDays(dayNum);
    }

    /**
     * 日期增减
     */
    public static LocalDateTime plusDays(LocalDateTime localDateTime, int dayNum) {
        return localDateTime.plusDays(dayNum);
    }

    /**
     * date：2013-01-01 10:00:00 返回：2013-01-01 00:00:00
     */
    public static LocalDateTime getStartOfDay(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        int dayOfMonth = localDateTime.getDayOfMonth();
        return LocalDateTime.of(year, month, dayOfMonth, 0, 0, 0, 0);
    }

    /**
     * date：2013-01-01 10:00:00 返回：2013-01-01 23:59:59
     */
    public static LocalDateTime getEndOfDay(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        int dayOfMonth = localDateTime.getDayOfMonth();
        return LocalDateTime.of(year, month, dayOfMonth, 23, 59, 59, 0);
    }

    /**
     * date：2022-02-04 10:00:00 返回：2022-02-01 00:00:00
     */
    public static LocalDateTime getStartOfMonth(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        return LocalDateTime.of(year, month, 1, 0, 0, 0, 0);
    }

    /**
     * date：2022-02-04 10:00:00 返回：2022-02-28 23:59:59
     */
    public static LocalDateTime getEndOfMonth(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        int length = month.length(toLocalDate(localDateTime).isLeapYear());
        return LocalDateTime.of(year, month, length, 23, 59, 59, 0);
    }

    /**
     * date：2022-02-04 10:00:00 返回：2022-01-01 00:00:00
     */
    public static LocalDateTime getStartOfYear(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        return LocalDateTime.of(year, Month.JANUARY, 1, 0, 0, 0, 0);
    }

    /**
     * date：2022-02-04 10:00:00 返回：2022-12-31 23:59:59
     */
    public static LocalDateTime getEndOfYear(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        return LocalDateTime.of(year, Month.DECEMBER, 31, 23, 59, 59, 0);
    }

    /**
     * 获取参数所在月的第一天
     */
    public static LocalDate getFirstDayOfMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取参数所在月的最后一天
     */
    public static LocalDate getLastDayOfMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取2个日期的天数差
     */
    public static long getAbsDifferDays(LocalDate date1, LocalDate date2) {
        return Math.abs(ChronoUnit.DAYS.between(date1, date2));
    }

    public static Date toDate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    public static Date toDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * 判断是否合法的日期
     */
    public static boolean isLegalDate(int year, int month, int day) {
        if (year < 1 || month < 1 || month > 12 || day < 1 || day > 31) {
            return false;
        }
        if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) {
            return false;
        }
        if (month == 2) {
            if ((year % 4 != 0) && day > 28) {
                return false;
            }
            if (year % 4 == 0) {
                if (year % 100 == 0 && year % 400 != 0) {
                    if (day > 28)
                        return false;
                } else if (day > 29) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 获取当前日期的字符串
     *
     * @param pattern --格式
     */
    public static String getCurrentDateStr(String pattern) {
        SimpleDateFormat sf = new SimpleDateFormat(pattern);
        Date dt = new Date(System.currentTimeMillis());
        return sf.format(dt);
    }

    /**
     * 字符串转为日期
     */
    public static Date strToDate(String dateStr, String pattern) {
        SimpleDateFormat sf = new SimpleDateFormat(pattern);
        try {
            return sf.parse(dateStr);
        } catch (ParseException e) {
            log.error(e.getMessage(), e);
            throw new CommonException(e.getMessage());
        }
    }

    public static Date strToDate(String dateStr) {
        return strToDate(dateStr, DateFormats.DATE_FORMAT);
    }

    public static Date strToTime(String dateStr) {
        return strToDate(dateStr, DateFormats.DATE_TIME_FORMAT);
    }

    /**
     * 格式化日期
     */
    public static String formatDate(Date date, String pattern) {
        if (date == null)
            return null;
        SimpleDateFormat sf = new SimpleDateFormat(pattern);
        return sf.format(date);
    }

    /**
     * 返回yyyy-MM-dd
     */
    public static String formatDate(Date date) {
        return formatDate(date, DateFormats.DATE_FORMAT);
    }

    /**
     * 返回yyyy-MM-dd HH:mm:ss
     */
    public static String formatTime(Date date) {
        return formatDate(date, DateFormats.DATE_TIME_FORMAT);
    }

    /**
     * 日期增减
     */
    public static Date addDay(Date date, int dayNum) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, dayNum);
        return calendar.getTime();
    }

    public static Date addHour(Date date, int hourNum) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, hourNum);
        return calendar.getTime();
    }

    public static Date add(Date date, int field, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(field, amount);
        return calendar.getTime();
    }

    /**
     * 获取自然周
     */
    public static String getCurrnetWeek() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int week = calendar.get(Calendar.WEEK_OF_YEAR);
        int year = calendar.get(Calendar.YEAR);
        if (week < 10) {
            return year + "0" + week;
        }
        return year + "" + week;
    }

    /**
     * date：2013-01-01 10:00:00 返回：2013-01-01 00:00:00
     */
    public static Date getStartOfDay(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        Calendar t = Calendar.getInstance();
        t.clear();
        t.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        return t.getTime();
    }

    /**
     * date：2013-01-01 10:00:00 返回：2013-01-01 23:59:59
     */
    public static Date getEndOfDay(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        Calendar t = Calendar.getInstance();
        t.clear();
        t.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        return t.getTime();
    }

    /**
     * 获取参数所在月的第一天
     */
    public static Date getFirstDayOfMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        Calendar t = Calendar.getInstance();
        t.clear();
        t.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), 1, 0, 0, 0);
        return t.getTime();
    }

    /**
     * 获取参数所在月的最后一天
     */
    public static Date getLastDayOfMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MONTH, 1);

        Calendar t = Calendar.getInstance();
        t.clear();
        t.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), 1);
        t.add(Calendar.DAY_OF_MONTH, -1);
        return t.getTime();
    }

    /**
     * 获取2个日期的天数差
     */
    public static int getAbsDifferDays(Date date1, Date date2) {
        long day = Math.abs((date1.getTime() - date2.getTime())) / (24L * 3600 * 1000);
        return (int) (Math.abs(day));
    }

    /**
     * 获取当前时间，精确到秒
     *
     * @return
     */
    public static Date nowDate() {
        return new Date(1000 * (System.currentTimeMillis() / 1000));
    }

    /**
     * 获取当前时间，精确到秒
     */
    public static LocalDateTime nowSecondLocalDateTime() {
        return LocalDateTime.now().withNano(0);
    }

    /**
     * 转换成精确到秒的时间
     */
    public static LocalDateTime secondLocalDateTime(LocalDateTime time) {
        int year = time.getYear();
        Month month = time.getMonth();
        int dayOfMonth = time.getDayOfMonth();
        int hour = time.getHour();
        int minute = time.getMinute();
        int second = time.getSecond();
        return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, 0);
    }

    /**
     * 返回开始时间和结束时间之间的每一天包含多少秒
     */
    public static TreeMap<LocalDate, Integer> getEachDayDurationSecond(LocalDateTime start, LocalDateTime end) {
        CommonException.isTrue(!end.isBefore(start), "end不能早于start");
        LocalDateTime secondStart = secondLocalDateTime(start);
        LocalDateTime secondEnd = secondLocalDateTime(end);
        long s = getMilli(secondStart);
        long e = getMilli(secondEnd);
        TreeMap<LocalDate, Integer> map = new TreeMap<>();
        if (s == e) {
            map.put(toLocalDate(secondStart), 0);
            return map;
        }
        while (s < e) {
            LocalDateTime tomorrow = secondStart.plusDays(1);
            long tomorrowMilli = getMilli(getStartOfDay(tomorrow));
            long endTime = Math.min(e, tomorrowMilli);
            int t = (int) (endTime / 1000 - s / 1000);
            map.put(toLocalDate(secondStart), t);
            secondStart = tomorrow;
            s = tomorrowMilli;
        }
        return map;
    }


    /**
     * 返回start和end之间，每天有多少毫秒
     */
    public static TreeMap<LocalDate, Long> getEachDayDurationMilli(LocalDateTime start, LocalDateTime end) {
        CommonException.isTrue(!end.isBefore(start), "end不能早于start");
        long s = getMilli(start);
        long e = getMilli(end);
        TreeMap<LocalDate, Long> map = new TreeMap<>();
        if (s == e) {
            map.put(toLocalDate(start), 0L);
            return map;
        }
        while (s < e) {
            LocalDateTime tomorrow = start.plusDays(1);
            long tomorrowMilli = getMilli(getStartOfDay(tomorrow));
            long endTime = Math.min(e, tomorrowMilli);
            long t = endTime - s;
            map.put(toLocalDate(start), t);
            start = tomorrow;
            s = tomorrowMilli;
        }
        return map;
    }

    /**
     * 返回开始时间和结束时间之间的每一天，包括start和end的
     */
    public static List<LocalDate> getRangeDayList(LocalDateTime start, LocalDateTime end) {
        CommonException.isTrue(!end.isBefore(start), "end不能早于start");
        start = getStartOfDay(start);
        end = getStartOfDay(end);
        LocalDateTime t = start;
        List<LocalDate> r = new LinkedList<>();
        while (!t.isAfter(end)) {
            r.add(toLocalDate(t));
            t = t.plusDays(1);
        }
        return r;
    }

    /**
     * 返回开始时间和结束时间之间的每个月的第一天，包括start和end的
     */
    public static List<LocalDate> getRangeMonthList(LocalDateTime start, LocalDateTime end) {
        CommonException.isTrue(!end.isBefore(start), "end不能早于start");
        start = getStartOfMonth(start);
        end = getEndOfMonth(end);
        LocalDateTime t = start;
        List<LocalDate> r = new LinkedList<>();
        while (!t.isAfter(end)) {
            r.add(toLocalDate(t));
            t = t.plusMonths(1);
        }
        return r;
    }

    private static int getCharIndex(char c) {
        int i = (int) c;
        if (i < 58) {
            // ascii码减去0的码
            return i - 48;
        }
        // ascii码减去A的码65加上位置10，所以减去55
        return i - 55;
    }
}
